{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The autoreload extension is already loaded. To reload it, use:\n",
      "  %reload_ext autoreload\n"
     ]
    }
   ],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'\n",
    "import sys\n",
    "sys.path.append('../')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_deepseek import ChatDeepSeek\n",
    "\n",
    "llm = ChatDeepSeek(\n",
    "    model=\"deepseek-chat\",\n",
    "    temperature=0,\n",
    "    max_tokens=None,\n",
    "    timeout=None,\n",
    "    max_retries=2,\n",
    "    api_key=\"sk-e0ebc15c2d124d2cad19536757701fc6\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**工具调用**允许聊天模型通过“调用工具”来响应给定的提示。\n",
    "\n",
    "请记住，虽然名称“工具调用”暗示模型直接执行某些操作，但实际上并非如此！模型仅生成工具的参数，而实际运行工具（或不运行）取决于用户。\n",
    "\n",
    "工具调用是一种通用技术，可以从模型生成结构化输出，即使您不打算调用任何工具，也可以使用它。一个用例示例是从非结构化文本中提取信息。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 工具模式是python函数（需要注意的是，为函数增加tool装饰器后，必须增加一段函数功能描述）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "@tool\n",
    "def add(a: int, b: int) -> int:\n",
    "    \"\"\" Add a and b \"\"\"\n",
    "    return a+b\n",
    "\n",
    "@tool\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\" Multiply a and b \"\"\"\n",
    "    return a*b\n",
    "\n",
    "py_tools = [add, multiply]\n",
    "\n",
    "llm_with_py_tools = llm.bind_tools(py_tools)\n",
    "\n",
    "query = \"3乘12等于多少\"\n",
    "from langchain_core.messages import HumanMessage\n",
    "messages = [HumanMessage(query)]\n",
    "resp = llm_with_py_tools.invoke(query)\n",
    "messages.append(resp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "for tool_call in resp.tool_calls:\n",
    "    selected_tool = {\"add\": add, \"multiply\": multiply}[tool_call[\"name\"].lower()]\n",
    "    tool_msg = selected_tool.invoke(tool_call)\n",
    "    messages.append(tool_msg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[HumanMessage(content='3乘12等于多少', additional_kwargs={}, response_metadata={}),\n",
       " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_0_65a7b06d-f817-41d2-9045-0ff729623a71', 'function': {'arguments': '{\"a\": 3, \"b\": 12}', 'name': 'multiply'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 213, 'total_tokens': 238, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 192}, 'prompt_cache_hit_tokens': 192, 'prompt_cache_miss_tokens': 21}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_3a5770e1b4_prod0225', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-3c9b324a-c0fb-4116-9018-ae543f48fd16-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': 'call_0_65a7b06d-f817-41d2-9045-0ff729623a71', 'type': 'tool_call'}], usage_metadata={'input_tokens': 213, 'output_tokens': 25, 'total_tokens': 238, 'input_token_details': {'cache_read': 192}, 'output_token_details': {}}),\n",
       " ToolMessage(content='36', name='multiply', tool_call_id='call_0_65a7b06d-f817-41d2-9045-0ff729623a71')]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后使用工具结果调用模型。模型将使用此信息生成对我们原始查询的最终答案。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 0, 'prompt_tokens': 244, 'total_tokens': 244, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 192}, 'prompt_cache_hit_tokens': 192, 'prompt_cache_miss_tokens': 52}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_3a5770e1b4_prod0225', 'finish_reason': 'stop', 'logprobs': None}, id='run-296171c5-5c87-434e-9085-c799b756e1e1-0', usage_metadata={'input_tokens': 244, 'output_tokens': 0, 'total_tokens': 244, 'input_token_details': {'cache_read': 192}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm_with_py_tools.invoke(messages)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
